#include "PreCompile.hpp"
#include "TestDirTree.hpp"

namespace BuildNG
{
  ///////////
  // TestDirectoryTree
  TestDirectoryTree::TestDirectoryTree(const Glib::ustring &xml_file_name)
  {
    Xml::Document doc(xml_file_name);

    Xml::Element *xml_root = doc.get_root_node();
    assert(xml_root->get_name() == "root-dir");
    root_dir = Glib::RefPtr<Dir>(new Dir(*xml_root,0));

    cur_dir = root_dir;
  }

  Glib::ustring TestDirectoryTree::get_root() const
  {
    return "/";
  }

  Glib::ustring TestDirectoryTree::get_cur_dir() const
  {
    return cur_dir->get_path();;
  }

  Glib::RefPtr<TestDirectoryTree::DirIt> TestDirectoryTree::get_dir_iterator(const Glib::ustring &path) const
  {
    std::vector<Glib::ustring> path_elems = Glib::Path::split_into_elements(path);

    Dir *dir;
    if(Glib::path_is_absolute(path))
    {
      dir = root_dir->select_dir(path_elems,0);
    }
    else
    {
      dir = cur_dir->select_dir(path_elems,0);
    }

    return Glib::RefPtr<DirIt>(new DirItImpl(dir->this_ptr<Dir>()));
  }

  TestDirectoryTree::Dir::Dir(const Xml::Element &xml,Dir *parent_dir)
    : parent_dir(parent_dir)
  {
    name = xml.get_attribute_value("name");

    Xml::ConstElementList children_xml = xml.get_child_elements();
    for(int i = 0;i < (int)children_xml.size();i++)
    {
      const Xml::Element *cur_elem = children_xml[i];
      assert(cur_elem);

      if(cur_elem->get_name() == "dir")
      {
        Glib::ustring dir_name = cur_elem->get_attribute_value("name");
        Glib::RefPtr<Dir> dir = Glib::RefPtr<Dir>(new Dir(*cur_elem,this));

        assert(child_dirs.find(dir_name) == child_dirs.end());
        child_dirs[dir_name] = dir;
      }
      else if(cur_elem->get_name() == "file")
      {
        files.insert(cur_elem->get_attribute_value("name"));
      }
      else
      {
        assert(!"error in xml file");
      }
    }
  }

  TestDirectoryTree::Dir* TestDirectoryTree::Dir::select_dir(
    const std::vector<Glib::ustring> &elements,int elem_index)
  {
    if(elem_index == (int)elements.size())
    {
      return this;
    }
    else
    {
      assert(elements[elem_index] != ".");
      assert(elements[elem_index] != "..");

      ChildDirsIt it = child_dirs.find(elements[elem_index]);
      if(it == child_dirs.end())
      {
        assert(!"dir not found");
      }

      Glib::RefPtr<Dir> child_dir = it->second;
      return child_dir->select_dir(elements,elem_index + 1);
    }
  }

  Glib::ustring TestDirectoryTree::Dir::get_path() const
  {
    if(parent_dir)
    {
      Glib::ustring parent_path = parent_dir->get_path();
      if(*parent_path.end() == '/')
        return "/" + name;
      else
        return Glib::build_filename(parent_dir->get_path(),name);
    }
    else
      return "/";
  }

  ////////////
  // TestDirectoryTree::DirItImpl
  TestDirectoryTree::DirItImpl::DirItImpl(Glib::RefPtr<Dir> dir)
  {
    this->dir = dir;
    state = State_UnInitiated;
  }

  bool TestDirectoryTree::DirItImpl::move_next()
  {
    switch(state)
    {
    case State_UnInitiated:
      dir_it = dir->child_dirs.begin();
      if(dir_it == dir->child_dirs.end())
      {
        files_it = dir->files.begin();
        if(files_it == dir->files.end())
        {
          state = State_Ended;
          return false;
        }

        state = State_IteratingFiles;
        return true;
      }
      state = State_IteratingDirs;
      return true;

    case State_IteratingDirs:
      dir_it++;
      if(dir_it == dir->child_dirs.end())
      {
        files_it = dir->files.begin();
        if(files_it == dir->files.end())
        {
          state = State_Ended;
          return false;
        }

        state = State_IteratingFiles;
        return true;
      }

      return true;


    case State_IteratingFiles:
      files_it++;
      if(files_it == dir->files.end())
      {
        state = State_Ended;
        return false;
      }

      state = State_IteratingFiles;
      return true;

    case State_Ended:
      assert(!"don't call move_next, after the iterator has ended");
      return false;
    }

    assert(!"invalid state flag");
    return false;
  }

  const Glib::ustring& TestDirectoryTree::DirItImpl::get_cur()
  {
    switch(state)
    {

    case State_IteratingDirs:
      return dir_it->second->name;
    case State_IteratingFiles:
      return *files_it;

    case State_UnInitiated:
    case State_Ended:
    default:
      assert(!"you can't call get_cur when the iterator is in this state");
      static Glib::ustring dum = "";
      return dum;
    }
  }

  bool TestDirectoryTree::DirItImpl::is_directory()
  {
    assert(state == State_IteratingDirs || state == State_IteratingFiles);
    return state == State_IteratingDirs;
  }
}
